package com.corgi.mybatis.interceptor.mybatis.result;

import com.corgi.mybatis.annotation.Encrypt;
import com.corgi.mybatis.annotation.EncryptField;
import com.corgi.mybatis.base.EncryptFieldStrategy;
import com.corgi.mybatis.util.ReflectHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;

import java.lang.reflect.Method;
import java.sql.Statement;
import java.util.*;

/**
 * @title: ResultInterceptor
 * @description: 结果加密
 * @author: dengmiao
 * @create: 2019-10-18 09:23
 **/
@Slf4j
@Intercepts(@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class}))
public class ResultEncryptInterceptor implements Interceptor {

    private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
    private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
    private static final ReflectorFactory REFLECTOR_FACTORY = new DefaultReflectorFactory();

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 获取到返回结果
        ResultSetHandler resultSetHandler = (ResultSetHandler) invocation.getTarget();
        MetaObject metaResultSetHandler = MetaObject.forObject(resultSetHandler, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, REFLECTOR_FACTORY);
        MappedStatement mappedStatement = (MappedStatement) metaResultSetHandler.getValue("mappedStatement");
        Encrypt annotation = getEncryptAnnotation(mappedStatement);
        Object returnValue = invocation.proceed();
        if (annotation != null && returnValue != null) {
            EncryptField[] fields = annotation.value();

            // 对结果处理
            try {
                // list集合
                if (returnValue instanceof List) {
                    List<?> list = (List) returnValue;
                    for (int i = 0; i < list.size(); i++) {
                        Object item = list.get(i);
                        for (EncryptField encryptField : fields) {
                            Object oldVal = ReflectHelper.getValueByFieldName(item, encryptField.field());
                            EncryptFieldStrategy strategy = encryptField.encryptStrategy().newInstance();
                            ReflectHelper.setValueByFieldName(item, encryptField.field(), strategy.encrypt(oldVal));
                        }
                    }
                }
                // fastjson的jsonarray待完成
                // 单体对象
                else {
                    for (EncryptField encryptField : fields) {
                        Object oldVal = ReflectHelper.getValueByFieldName(returnValue, encryptField.field());
                        EncryptFieldStrategy strategy = encryptField.encryptStrategy().newInstance();
                        ReflectHelper.setValueByFieldName(returnValue, encryptField.field(), strategy.encrypt(oldVal));
                    }
                }
            } catch (Exception e) {
                // 打印日志
                log.error(e.getMessage());
            }
        }
        return returnValue;
    }

    @Override
    public Object plugin(Object o) {
        return Plugin.wrap(o, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

    /**
     * 获取方法上的Encrypt注解
     * @param mappedStatement MappedStatement
     * @return Encrypt注解
     */
    private Encrypt getEncryptAnnotation(MappedStatement mappedStatement) {
        Encrypt encrypt = null;
        try {
            String id = mappedStatement.getId();
            String className = id.substring(0, id.lastIndexOf("."));
            String methodName = id.substring(id.lastIndexOf(".") + 1);
            final Method[] method = Class.forName(className).getMethods();
            for (Method me : method) {
                if (me.getName().equals(methodName) && me.isAnnotationPresent(Encrypt.class)) {
                    encrypt = me.getAnnotation(Encrypt.class);
                    break;
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return encrypt;
    }
}
